package com.wdciti.sccp.iot.springvertx.protocols.tcp.ext;

import io.vertx.core.MultiMap;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonObject;
import io.vertx.core.net.SocketAddress;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;
import java.util.UUID;

/**
 * @author by early
 * @blame IOT Team
 * @date 2022/05/10.
 */
@Slf4j
public class EndpointHeadersAdaptor {

    public static final String MSG_ID = "ep.msgId";
    public static final String VERSION = "ep.version";
    public static final String SEND_OR_RECEIVE = "ep.rs";
    public static final String REMOTE = "ep.remote";
    public static final String LOCAL = "ep.local";
    public static final String HOST = "host";
    public static final String PORT = "port";
    public static final String IP = "ip";
    public static final String PATH = "path";
    public static final String BUS_ADDR = "ep.busAddr";
    public static final String BUS_ADDR_TAIL = "ep.busTail";
    public static final String QUALITY_OF_BUS = "ep.qob";
    private final MultiMap headers;

    public EndpointHeadersAdaptor(MultiMap headers) {
        this.headers = headers;
    }

    public EndpointHeadersAdaptor() {
        this.headers = MultiMap.caseInsensitiveMultiMap();
    }

    public static EndpointHeadersAdaptor emptyHeaders() {
        return newInstance(UUID.randomUUID().toString());
    }

    public static EndpointHeadersAdaptor newInstance(@NonNull String uuid) {
        EndpointHeadersAdaptor header = new EndpointHeadersAdaptor();
        header.headers.add(MSG_ID, uuid).add(VERSION, "-");
        return header;
    }

    public static EndpointHeadersAdaptor instanceFrom(MultiMap headers) {
        if (headers == null || headers.isEmpty()) {
            return EndpointHeadersAdaptor.emptyHeaders();
        } else {
            EndpointHeadersAdaptor instance = new EndpointHeadersAdaptor();
//            if (!headers.contains(MSG_ID)) {
//                throw new Exception("No MsgId in Headers!");
//            }
//            if (!headers.contains(BUS_ADDR) || !headers.contains(BUS_ADDR_TAIL)) {
//                throw new Exception("No BusAddress in Endpoint Headers!");
//            }
//            if (!headers.contains(BUS_ADDR_TAIL)) {
//                throw new Exception("No BusAddressTail in Endpoint Headers!");
//            }
            instance.headers().addAll(headers);
            return instance;
        }

    }

    public MultiMap headers() {
        return headers;
    }

    public String getMsgId() {
        return headers.get(MSG_ID);
    }

    public EndpointHeadersAdaptor setMsgId(@NonNull String uuid) {
        headers.set(MSG_ID, uuid);
        return this;
    }

    public String get(String name) {
        return headers.get(name);
    }

    public String get(String name, String defaultValue) {
        String value = headers.get(name);
        return value == null ? defaultValue : value;
    }

    public EndpointHeadersAdaptor addRemoteAddress(SocketAddress address) {
        if (address != null) {
            addAddress(address, true);
        }
        return this;
    }

    public EndpointHeadersAdaptor addLocalAddress(SocketAddress address) {
        if (address != null) {
            addAddress(address, false);
        }
        return this;
    }

    public EndpointHeadersAdaptor addRorS(RS rs) {
        headers.add(SEND_OR_RECEIVE, rs.name());
        return this;
    }

    public EndpointHeadersAdaptor addBusAddr(@NonNull String busAddr, @NonNull String tail) {
        headers.add(BUS_ADDR, busAddr).add(BUS_ADDR_TAIL, tail);
        return this;
    }

    public EndpointHeadersAdaptor addBusQob(@NonNull EndpointHeadersAdaptor.QOB qos) {
        headers.add(QUALITY_OF_BUS, qos.name());
        return this;
    }

    public QOB getBusQos() {
        String qos = headers.get(QUALITY_OF_BUS);
        if (qos == null) {
            return QOB.PUBLISH;
        } else {
            return QOB.valueOf(qos);
        }
    }

    public RS getRorS() {
        String rsn = headers.get(SEND_OR_RECEIVE);
        if (rsn == null) {
            return RS.N;
        } else {
            return RS.valueOf(rsn);
        }
    }

    public String getBusAddr() {
        return headers.get(BUS_ADDR);
    }

    public String getBusAddrTail() {
        return headers.get(BUS_ADDR_TAIL);
    }

    public SocketAddress getLocalAddress() {
        try {
            return getSocketAddress(LOCAL);
        } catch (Exception e) {
            log.warn("getSocketAddress failed cause:" + e.getMessage(), e);
            return null;
        }
    }

    public SocketAddress getRemoteAddress() {
        try {
            return getSocketAddress(REMOTE);
        } catch (Exception e) {
            log.warn("getSocketAddress failed cause:" + e.getMessage(), e);
            return null;
        }
    }

    private SocketAddress getSocketAddress(String name) {
        Map json = Json.decodeValue(headers.get(name), Map.class);
        if (json.containsKey(PATH)) {
            return SocketAddress.domainSocketAddress(json.get(PATH).toString());
        } else {
            int port = Integer.parseInt(json.get(PORT).toString());
            String ip = json.get(IP).toString();
            String host = json.get(HOST).toString();
            return SocketAddress.inetSocketAddress(port, ip == null ? host : ip);
        }
    }

    private void addAddress(@NonNull SocketAddress address, boolean isRemote) {
        JsonObject addrJson = new JsonObject();
        if (address.isInetSocket()) {
            addrJson.put(IP, address.hostAddress()).put(PORT, address.port()).put(HOST, address.host());
        } else if (address.isDomainSocket()) {
            addrJson.put(PATH, address.path());
        }
        if (isRemote) {
            headers.add(REMOTE, addrJson.toString());
        } else {
            headers.add(LOCAL, addrJson.toString());
        }
    }

    public void remove(String name) {
        headers.remove(name);
    }

    public enum RS {R, S, N}

    public enum QOB {PUBLISH, SEND, REQUEST}
}
